﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;

using System.Security.Cryptography;

namespace XCommunity.Web
{
  internal sealed class AccountCrypto : IDisposable
  {

    private readonly HashAlgorithm _hashAlgorithm = new MD5CryptoServiceProvider();
    private static readonly HashAlgorithm _passwordHash = new MD5CryptoServiceProvider();

    private static readonly Encoding _encoding = Encoding.UTF8;

    private static Random random = new Random( DateTime.Now.Millisecond );



    private const string key = "fnuiwbr238odw8obvgy23otde78wgdsuiogt78dwo3rug8dsfsdu";
    private const string iv = "dsuirhrwpfd80sfehw3rb2uioef7wfwehrfdksioh3fe3spfhea9";

    private ICryptoTransform encryptor;
    private ICryptoTransform decryptor;


    public AccountCrypto()
    {
      InitCryptography();
    }




    internal static byte[] ComputePasswordHash( string password )
    {
      return _passwordHash.ComputeHash( _encoding.GetBytes( password ) );
    }

    internal static Random Random
    {
      get { return random; }
    }


    /// <summary>
    /// 初始化加密解密器
    /// </summary>
    public void InitCryptography()
    {
      RijndaelManaged crypto = new RijndaelManaged();

      crypto.Key = _hashAlgorithm.ComputeHash( Encoding.Unicode.GetBytes( key ) );
      crypto.IV = _hashAlgorithm.ComputeHash( Encoding.Unicode.GetBytes( iv ) );

      encryptor = crypto.CreateEncryptor();
      decryptor = crypto.CreateDecryptor();
    }



    /// <summary>
    /// 加密编码到一个字符串
    /// </summary>
    /// <param name="username">用户名</param>
    /// <param name="passwordHashed">哈希后的密码</param>
    /// <returns>加密后的字符串</returns>
    internal string EncryptToString( string username, byte[] passwordHashed )
    {

      StringBuilder builder = new StringBuilder( username );
      builder.Append( "%" );

      byte[] password1 = ApplyRandomByte( passwordHashed );
      byte[] password2 = encryptor.TransformFinalBlock( password1, 0, password1.Length );
      byte[] password3 = ApplyRandomByte( password2 );
      byte[] password4 = HashSign( password3 );

      builder.Append( Convert.ToBase64String( password4 ) );

      return builder.ToString();
    }



    /// <summary>
    /// 解密出用户登录信息
    /// </summary>
    /// <param name="encryptString">加密字符串</param>
    /// <param name="username">解密出来的用户名</param>
    /// <param name="password">解密出来的密码</param>
    /// <returns>登陆好的用户帐户</returns>
    internal bool DecryptAccount( string encryptString, out string username, out byte[] password )
    {
      username = null;
      password = null;

      string[] strs = encryptString.Split( '%' );
      username = strs[0];

      try
      {
        byte[] password1 = Convert.FromBase64String( strs[1] );
        byte[] password2 = DeHash( password1 );
        byte[] password3 = RemoveRandomByte( password2 );
        byte[] password4 = decryptor.TransformFinalBlock( password3, 0, password3.Length );
        password = RemoveRandomByte( password4 );

        return true;
      }
      catch
      {
        return false;
      }
    }



    private byte[] ApplyRandomByte( byte[] data )
    {
      byte[] _data = new byte[data.Length + 2];

      data.CopyTo( _data, 1 );

      _data[0] = (byte) random.Next( byte.MaxValue );
      _data[_data.Length - 1] = (byte) random.Next( byte.MaxValue );

      return _data;
    }

    private byte[] RemoveRandomByte( byte[] data )
    {

      byte[] _data = new byte[data.Length - 2];
      Array.Copy( data, 1, _data, 0, data.Length - 2 );
      return _data;
    }




    /// <summary>
    /// 对数据进行哈希签名
    /// </summary>
    /// <param name="input">数据</param>
    /// <returns></returns>
    private byte[] HashSign( byte[] input )
    {
      try
      {
        if ( input.Length <= 0 )
          throw new ArgumentException( "数据不够", "input" );

        byte[] hash;
        hash = _hashAlgorithm.ComputeHash( input );

        byte[] result = new byte[input.Length + 16];

        int index = hash[0] % 16 + 1;
        input.CopyTo( result, index );

        int i = 0;
        for ( ; i < index; i++ )//插在前面的哈希效验
          result[i] = hash[i];

        for ( int j = input.Length + index; j < result.Length; j++, i++ )//插在后面的哈希效验
          result[j] = hash[i];

        return result;
      }
      catch
      {
        return null;
      }
    }


    /// <summary>
    /// 验证数据的哈希，并剥离签名信息
    /// </summary>
    /// <param name="input">已签名的数据</param>
    /// <returns>源数据</returns>
    private byte[] DeHash( byte[] input )
    {
      try
      {
        if ( input.Length <= 16 )
          throw new ArgumentException( "非法的数据", "input" );

        int index = input[0] % 16 + 1;
        byte[] hash = new byte[16];//哈希信息
        byte[] content = new byte[input.Length - 16];//内容

        int j = 0;
        int k = 0;
        for ( int i = 0; i < input.Length; i++ )//解出内容和哈希信息
        {
          if ( i < index || i >= input.Length - 16 + index )
          {
            hash[j] = input[i];
            j++;
          }
          else
          {
            content[k] = input[i];
            k++;
          }
        }

        byte[] hash_;
        hash_ = _hashAlgorithm.ComputeHash( content );//效验哈希
        for ( int i = 0; i < hash.Length; i++ )
        {
          if ( hash[i] != hash_[i] )
            return null;
        }
        return content;
      }
      catch
      {
        return null;
      }
    }

    public void Dispose()
    {
      IDisposable __hash = _hashAlgorithm as IDisposable;
      IDisposable __encryptor = encryptor as IDisposable;
      IDisposable __decryptor = decryptor as IDisposable;

      if ( __hash != null )
        __hash.Dispose();

      if ( __encryptor != null )
        __encryptor.Dispose();

      if ( __decryptor != null )
        __decryptor.Dispose();
    }



  }
}